﻿
using RM.BasicLibrary.Resources;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.Entity;
using System.Data.SqlClient;
using static RM.BasicLibrary.Models.Databases.MSDatabase;

namespace RM.BasicLibrary.Models.Databases
{
    /// <summary>
    /// 
    /// </summary>
    public abstract class BaseDatabase : IDatabase
    {
        public DbContext DBcontext { get; set; }
        private bool isClose = true;
        protected bool isTransaction = false;
        protected DbCommand dbCommand = null;
        protected CommandType CommandType { get; set; } = CommandType.Text;
        protected string CmdText { get; set; } = string.Empty;
        protected System.Collections.Generic.List<DbParameter> Parameters { get; set; } = new System.Collections.Generic.List<DbParameter>();
        public abstract string ConnectionString { get; set; }

        protected DbConnection DbConnectionIns = null;
        /// <summary>
        /// 设置连接字符串
        /// </summary>
        /// <param name="connectionString"></param>
        public void SetDatabase(string connectionString)
        {
            if (!connectionString.IsNull()) this.ConnectionString = connectionString;
        }
    
        public virtual DbContext CreateDbContext(string connectionString = null)
        {
            this.ConnectionString.NotNull();
            DBcontext = new DatabaseContext(ConnectionString);
            return DBcontext;
        }
        /// <summary>
        /// 
        /// </summary>
        protected void DisposeConnection()
        {
            Root.Ins.ExFilter.Execute(() =>
            {
                dbCommand?.Dispose();
            });
            Root.Ins.ExFilter.Execute(() =>
            {
                DbConnectionIns?.Close();
                DbConnectionIns?.Dispose();
            });

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="cmdText"></param>
        /// <param name="type"></param>
        /// <param name="parm"></param>
        private void PrepareParams(string cmdText, CommandType type = CommandType.Text, params DbParameter[] parm)
        {
            Parameters.Clear();
            this.CmdText = cmdText;
            CommandType = type;
            if (parm != null)
                Parameters.AddRange(parm);
            if (ConnectionString.IsNull()) throw new BasicLibraryException(Root.BasicIns.GetValue(ModelsResource.ConnectionStringIsNull));
            if (CmdText.IsNull()) throw new BasicLibraryException(Root.BasicIns.GetValue(ModelsResource.ConnectionStringIsNull));
            isClose = true;
        }
        /// <summary>
        ///  DbConnectionIns = new SqlConnection(ConnectionString);
        /// dbCommand = new SqlCommand();
        /// </summary>
        protected virtual void CreateConnection()
        {
            DisposeConnection();
            DbConnectionIns = new SqlConnection(ConnectionString);
            dbCommand = new SqlCommand();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="tranAction"></param>
        public void Transaction(Action<DbCommand> tranAction)
        {

            CreateConnection();
            DbConnectionIns?.Open();
            dbCommand.CommandType = CommandType.Text;
            dbCommand.Connection = DbConnectionIns;
            DbTransaction trans = DbConnectionIns.BeginTransaction();
            dbCommand.Transaction = trans;
            Root.Ins.ExFilter.Execute(() =>
            {
                tranAction(dbCommand);
                trans.Commit();
                DisposeConnection();
            },
                () =>
                {
                    trans.Rollback();
                    DisposeConnection();
                },
                Root.BasicIns.GetValue(ModelsResource.DatabaseTransactionExecutionFailed));
            DisposeConnection();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="tranAction"></param>
        /// <returns></returns>
        public dynamic Execute(Func<DbCommand, dynamic> tranAction)
        {
            CreateConnection();
            dynamic returnValue = null;
            DbConnectionIns.Open();
            dbCommand.CommandType = CommandType.Text;
            dbCommand.Connection = DbConnectionIns;
            dbCommand.Parameters.AddRange(Parameters.ToArray());
            dbCommand.CommandText = CmdText;
            Root.Ins.ExFilter.Execute(() => { returnValue = tranAction(dbCommand); if (isClose) DisposeConnection(); },
                () => DisposeConnection(), Root.BasicIns.GetValue(ModelsResource.DatabaseExecutionFailed));
            if (isClose)
                DisposeConnection();

            return returnValue;
        }
        #region ISqlHander

        /// <summary>
        /// Execute sql, return the affected row
        /// </summary>
        /// <param name="cmdText">sql command text</param>
        /// <param name="type">command type</param>
        /// <param name="parm">Parameter Collection </param>
        /// <returns></returns>
        public int ExecuteNonQuery(string cmdText, CommandType type = CommandType.Text, params DbParameter[] parm)
        {
            PrepareParams(cmdText, type, parm);
            return (int)Execute((x) => x.ExecuteNonQuery());

        }
        /// <summary>
        /// Execute sql, return the SqlDataReader
        /// </summary>
        /// <param name="cmdText">sql command text</param>
        /// <param name="type">command type</param>
        /// <param name="parm">Parameter Collection </param>
        /// <returns></returns>
        public DbDataReader ExecuteReader(string cmdText, CommandType type = CommandType.Text, params DbParameter[] parm)
        {
            PrepareParams(cmdText, type, parm);
            isClose = false;
            return (DbDataReader)Execute((x) => x.ExecuteReader(CommandBehavior.CloseConnection));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="cmdText"></param>
        /// <param name="type"></param>
        /// <param name="parm"></param>
        /// <returns></returns>
        public T ExecuteReader<T>(string cmdText, CommandType type = CommandType.Text, params DbParameter[] parm) where T : new()
        {
            PrepareParams(cmdText, type, parm);
            dynamic dynamic = null;
            Execute((x) =>
            {
                var reader = x.ExecuteReader(CommandBehavior.CloseConnection);
                dynamic = Root.Ins.DataConvert.ToEntity<T>(reader);
                return reader;
            });
            return (T)dynamic;
        }
        public IEnumerable<T> ExecuteReaders<T>(string cmdText, CommandType type = CommandType.Text, params DbParameter[] parm) where T : new()
        {
            PrepareParams(cmdText, CommandType.Text, parm);
            dynamic dynamic = null;
            Execute((x) =>
             {
                 var reader = x.ExecuteReader(CommandBehavior.CloseConnection);
                 dynamic = Root.Ins.DataConvert.ToEntitys<T>(reader);
                 return reader;
             });
            return dynamic as IEnumerable<T>;
        }
        /// <summary>
        /// Execute sql, return the object
        /// </summary>
        /// <param name="cmdText">sql command text</param>
        /// <param name="type">command type</param>
        /// <param name="parm">Parameter Collection </param>
        /// <returns></returns>
        public object ExecuteScalar(string cmdText, CommandType type = CommandType.Text, params DbParameter[] parm)
        {
            PrepareParams(cmdText, type, parm);
            return (object)Execute((x) => x.ExecuteScalar());

        }
        /// <summary>
        /// Execute sql, return the DataTable
        /// </summary>
        /// <param name="cmdText">sql command text</param>
        /// <param name="type">command type</param>
        /// <param name="parm">Parameter Collection </param>
        /// <returns></returns>
        public virtual DataTable GetDataTable(string cmdText, CommandType type = CommandType.Text, params DbParameter[] parm)
        {
            DataTable dt = new DataTable();
            using (SqlDataAdapter adapter = new SqlDataAdapter(cmdText, ConnectionString))
            {
                adapter.SelectCommand.CommandType = type;
                adapter.SelectCommand.Parameters.AddRange(parm);
                adapter.Fill(dt);
                return dt;
            }
        }


        #endregion


    }
}
